using System;
using System.Collections.Generic;
using System.Text;

namespace Noein.GeneticAlgorithm
{
    /// <summary>
    /// Crossover two or more parent individuals' genotype to produce a
    /// offspring individual.
    /// </summary>
    /// <typeparam name="TGenotype"><see>Individual</see></typeparam>
    /// <typeparam name="TPhenotype"><see>Individual</see></typeparam>
    /// <param name="parents">multiple parent individuals</param>
    /// <returns></returns>
    public delegate Individual<TGenotype, TPhenotype> Crossover<TGenotype, TPhenotype>(params Individual<TGenotype, TPhenotype>[] parents);
    
    /// <summary>
    /// Mutate a single parent individual's genotype to produce a offspring
    /// individual.
    /// </summary>
    /// <typeparam name="TGenotype"><see>Individual</see></typeparam>
    /// <typeparam name="TPhenotype"><see>Individual</see></typeparam>
    /// <param name="parent">single parent individual</param>
    /// <returns></returns>
    public delegate Individual<TGenotype, TPhenotype> Mutation<TGenotype, TPhenotype>(Individual<TGenotype, TPhenotype> parent);

    /// <summary>
    /// Determines the fitness of the output generated by the problem function.
    /// </summary>
    /// <typeparam name="TOutput"><see>Problem</see></typeparam>
    /// <param name="output">output generated by feeding solution to a problem</param>
    /// <returns></returns>
    //public delegate float FitnessFunction<TOutput>(TOutput output);

    /// <summary>
    /// Encapsulates the selection and reproduction process.
    /// </summary>
    /// <typeparam name="TGenotype"><see>Individual</see></typeparam>
    /// <typeparam name="TPhenotype"><see>Individual</see></typeparam>
    public class Selection<TGenotype, TPhenotype, TOutput>
    {
        protected Crossover<TGenotype, TPhenotype> Cross;
        protected Mutation<TGenotype, TPhenotype> Mutate;
        //protected FitnessFunction<TOutput> fitness;

        /// <summary>
        /// Out of the total number of offspring produced, the percentage that are produced using crossover, e.g.
        /// 0.75 = 75% offsprings will be produced using crossover, 25% will be produced using mutation.
        /// Suggested value: 75%
        /// </summary>
        private float crossoverRate = 0.75f;

        /// <summary>
        /// By how much does mutation affect the genotype. Suggested value: 0.001%
        /// </summary>
        private float mutationRate = 0.001f;

        /// <summary>
        /// Number of offsprings created per generation.
        /// </summary>
        private int offspringSize;

        // TODO
        // parent selection function
        // survival selection function

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="crossover">specifies the crossover function</param>
        /// <param name="mutation">specifies the mutation function</param>
        public Selection(
            float crossoverRate,
            float mutationRate,
            int offspringSize,
            Crossover<TGenotype, TPhenotype> crossover, 
            Mutation<TGenotype, TPhenotype> mutation)
            //FitnessFunction<TOutput> fitness)
        {
            //this.elitism = elitism;
            this.crossoverRate = crossoverRate;
            this.mutationRate = mutationRate;
            this.offspringSize = offspringSize;
            
            Cross = crossover;
            Mutate = mutation;
            //fitness = fitness;
        }

        /// <summary>
        /// Given a population of individuals, select the parents and produce the
        /// next generation of individuals.
        /// </summary>
        /// <param name="population">a population with one or more generations</param>
        /// <returns>a new generation of population</returns>
        public List<Individual<TGenotype, TPhenotype>> NextGeneration(Population<TGenotype, TPhenotype> population)
        {
            List<Individual<TGenotype, TPhenotype>> offsprings = new List<Individual<TGenotype, TPhenotype>>();
            
            int offspringIndex = 0;

            // crossover reproduction
            for (; offspringIndex < offspringSize * crossoverRate; offspringIndex++)
            {
                // TODO parent selection algorithm here
                Individual<TGenotype, TPhenotype>[] parents = population.RandomIndividuals(2);

                offsprings.Add(Cross(parents));            
            }

            // mutation reproduction
            for (; offspringIndex < offspringSize; offspringIndex++)
            {
                // TODO parent selection algorithm here
                Individual<TGenotype, TPhenotype> parent = population.RandomIndividuals(1)[0];

                offsprings.Add(Mutate(parent));
            }

            return offsprings;
        }
    }
}
